#include "AtNetworkDevice.h"

// urc栈堆大小
#define AT_URC_STACK sizeof(AtNetworkUrc *)

/**
 * 判断字符串是否以指定内容结尾
 * @param s0 被判断字符串
 * @param s1 字符串条件
 */
bool end_with(AtKey s0, AtKey s1, size_t l0, size_t l1) {
  for (size_t i = 1; i <= l1; i++) {
    char ch0 = s0[l0 - i];
    char ch1 = s1[l1 - i];

    if (ch0 != ch1) {
      return false;
    }
  }

  return true;
}

/**
 * 等待串口响应数据符合对应的条件(以r结尾)
 * @param expire_time 超时时间
 * @param k0 条件0-返回AT_RCODE1
 * @param k1 条件1-返回AT_RCODE2
 * @param k2 条件2-返回AT_RCODE3
 * @param k3 条件3-返回AT_RCODE4
 * @param k4 条件4-返回AT_RCODE5
 */
int8_t AtNetworkDevice::wait0(uint32_t expire_time, AtKey k0, AtKey k1, AtKey k2, AtKey k3, AtKey k4) {
  while (this->buffer_len < this->buffer_size && expire_time > millis()) {
    // ---------------------------------------------------------
    // - 读取一个字节的数据
    // ---------------------------------------------------------
    if (this->buffer_write_byte(this->_stream->read()) <= 0) {
      AT_YIELD();

      continue;
    }

    // ---------------------------------------------------------
    // - 检查是否为URC包，处理完后重置缓冲区
    // ---------------------------------------------------------
    for (size_t uindex = 0; uindex < this->usize; uindex++) {
      AtNetworkUrc *urcItem = this->ustack[uindex];
      if (urcItem != 0 && urcItem->urcCmd != 0) {
        if (this->buffer_end(urcItem->urcCmd)) {
          urcItem->callback(this);

          this->buffer_len = 0;

          AT_YIELD();

          break;
        }
      }
    }

    // ---------------------------------------------------------
    // - 检查是否为指定的返回数据类型
    // ---------------------------------------------------------
    if (k0 != NULL && this->buffer_end(k0)) {
      return AT_RCODE1;
    }
    if (k1 != NULL && this->buffer_end(k1)) {
      return AT_RCODE2;
    }
    if (k2 != NULL && this->buffer_end(k2)) {
      return AT_RCODE3;
    }
    if (k3 != NULL && this->buffer_end(k3)) {
      return AT_RCODE4;
    }
    if (k4 != NULL && this->buffer_end(k4)) {
      return AT_RCODE5;
    }

    AT_YIELD();
  }

  return AT_NETWORK_TIMEOUT;
}

/**
 * 移除回调设置
 */
bool AtNetworkDevice::remove_urc_callback(AtNetworkUrc *at_urc) {
  for (size_t i = 0; i < this->usize; i++) {
    if (this->ustack[i] == at_urc) {
      this->ustack[i] = NULL;

      return true;
    }
  }

  return false;
}

/**
 * 添加回调设置
 */
bool AtNetworkDevice::add_urc_callback(AtNetworkUrc *at_urc) {
  for (uint8_t i = 0; i < this->usize; i++) {
    if (this->ustack[i] == at_urc) {
      return true;
    }
  }

  for (uint8_t i = 0; i < this->usize; i++) {
    if (this->ustack[i] == NULL) {
      this->ustack[i] = at_urc;

      return true;
    }
  }

  return false;
}

/**
 * 缓冲区-写入一个字节
 */
int AtNetworkDevice::buffer_write_byte(int data) {
  if (data == -1 || data == 0) {
    return data;
  }
  if (DEBUG_OUT && data > 0) {
    Serial.write(data);
  }

  // 将数据写入缓冲区
  size_t i = this->buffer_len;

  this->buffer[i] = data;
  this->buffer_len += 1;

  return data;
}

/**
 * 缓冲区-判断是否以指定内容结尾
 */
bool AtNetworkDevice::buffer_end(AtKey suffix) {
  if (strlen(suffix) < this->buffer_len) {
    return end_with(this->buffer, suffix, //
                    this->buffer_len,     //
                    strlen(suffix));
  }

  return false;
}

/**
 * 尝试获取互斥锁
 */
bool AtNetworkDevice::device_lock(uint32_t timeout) {
  uint32_t time = millis();

  while (millis() - time < timeout) {
    if (this->lock->try_lock()) {
      return true;
    }

    delay(5);
  }

  return false;
}

/**
 * 释放锁
 */
void AtNetworkDevice::device_unlock() {
  this->lock->unlock();
}

AtNetworkDevice::AtNetworkDevice(Stream *stream, size_t bs, size_t us) {
  this->ustack = (AtNetworkUrc **)MEM_ALLOC(AT_URC_STACK * us);

  this->imei = (GSM_STRING)MEM_ALLOC(GSM_SIZE_IMEI);
  this->ipv4 = (GSM_STRING)MEM_ALLOC(GSM_SIZE_IPV4);
  this->ipv6 = (GSM_STRING)MEM_ALLOC(GSM_SIZE_IPV6);
  this->buffer = (GSM_STRING)MEM_ALLOC(bs);

  memset(this->ustack, 0, AT_URC_STACK * us);
  memset(this->imei, 0, GSM_SIZE_IMEI);
  memset(this->ipv4, 0, GSM_SIZE_IPV4);
  memset(this->ipv6, 0, GSM_SIZE_IPV6);
  memset(this->buffer, 0, bs);

  this->lock = new std::mutex();
  this->_stream = stream;
  this->buffer_size = bs;
  this->buffer_len = 0;
  this->usize = us;
}

AtNetworkDevice::~AtNetworkDevice() {
  delete this->lock;

  free(this->buffer);
  free(this->ustack);

  free(this->imei);
  free(this->ipv6);
  free(this->ipv4);
}